﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics.Contracts;

namespace ReactiveLang.Collections
{
    using Util;

    public interface IEnumerableExpression<out T> : INode<IEnumerableExpressionParent<T>>
    {
    }

    public static partial class EnumerableExpression
    {
        #region Create

        class AnonymousObservableEnumerable<T> : IEnumerableExpression<T>
        {
            internal Func<IEnumerableExpressionParent<T>, IDisposable> _activate;
            public IDisposable Activate(IEnumerableExpressionParent<T> parent)
            {
                return _activate(parent);
            }
        }
        public static IEnumerableExpression<T> CreateWithDisposable<T>(Func<IEnumerableExpressionParent<T>, IDisposable> activate)
        {
            Contract.Requires<ArgumentNullException>(activate != null, "activate");
            return new AnonymousObservableEnumerable<T> { _activate = activate };
        }
        public static IEnumerableExpression<T> Create<T>(Func<IEnumerableExpressionParent<T>, Action> activate)
        {
            Contract.Requires<ArgumentNullException>(activate != null, "activate");
            return CreateWithDisposable<T>(p => Disposable.Create(activate(p)));
        }

        #endregion

        #region Activate

        public static IDisposable ActivateWithDisposable<T>(this IEnumerableExpression<T> source, Func<IExpression<T>, int, IDisposable> onItem,
            Action onCleared, Action<Exception> onException)
        {
            Contract.Requires<ArgumentNullException>(source != null, "source");
            Contract.Requires<ArgumentNullException>(onItem != null, "onItem");
            Contract.Requires<ArgumentNullException>(onCleared != null, "onCleared");
            Contract.Requires<ArgumentNullException>(onException != null, "onException");
            return source.Activate(EnumerableExpressionParent.Create(onItem, onCleared, onException));
        }
        public static IDisposable ActivateWithDisposable<T>(this IEnumerableExpression<T> source, Func<IExpression<T>, int, IDisposable> onItem,
            Action onCleared)
        {
            Contract.Requires<ArgumentNullException>(source != null, "source");
            Contract.Requires<ArgumentNullException>(onItem != null, "onItem");
            Contract.Requires<ArgumentNullException>(onCleared != null, "onCleared");
            return source.ActivateWithDisposable(onItem, onCleared, ex => { throw ex; });
        }

        public static IDisposable Activate<T>(this IEnumerableExpression<T> source, Func<IExpression<T>, int, Action> onItem,
            Action onCleared, Action<Exception> onException)
        {
            Contract.Requires<ArgumentNullException>(source != null, "source");
            Contract.Requires<ArgumentNullException>(onItem != null, "onItem");
            Contract.Requires<ArgumentNullException>(onCleared != null, "onCleared");
            Contract.Requires<ArgumentNullException>(onException != null, "onException");
            return source.ActivateWithDisposable((item, i) => Disposable.Create(onItem(item, i)), onCleared, onException);
        }
        public static IDisposable Activate<T>(this IEnumerableExpression<T> source, Func<IExpression<T>, int, Action> onItem,
            Action onCleared)
        {
            Contract.Requires<ArgumentNullException>(source != null, "source");
            Contract.Requires<ArgumentNullException>(onItem != null, "onItem");
            Contract.Requires<ArgumentNullException>(onCleared != null, "onCleared");
            return source.Activate(onItem, onCleared, ex => { throw ex; });
        }

        #endregion

        internal static IEnumerableExpression<T> HideIdentity<T>(this IEnumerableExpression<T> source)
        {
            Contract.Requires<ArgumentNullException>(source != null, "source");
            return CreateWithDisposable<T>(source.Activate);
        }
    }
}
